//
// Created by QU on 24-6-25.
//
#include <iostream>
#include <functional>
#include <utility>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include <new>
#include <thread>
#include <algorithm>
#include <array>
#include <string>
#include <deque>

using std::cout, std::cin, std::endl;

// GCC添加了一个扩展关键字__attribute__，这个关键字前后都有双下画线并且紧跟着两对括号，用如此烦琐的语法作为说
// 明符的目的一方面是防止入侵C++标准，另一方面是避免和现有代码发生冲突。
//        __attribute__((attribute-list))

// GCC的属性语法十分灵活，它能够用于结构体、类、联合类型、枚举类型、变量或者函数。比如前面介绍的设置对齐字节长度就是GCC的属性语法：

/*
#define PRINT_ALIGN(c, v)   \
        std::cout << "alignof(" #c ") = " << alignof(c) \
        << ", alignof(" #v ") = " << alignof(v) << std::endl

__attribute__((aligned(16))) class X { int i; } a;

class __attribute__((aligned(16))) X1 { int i; } a1;

class X2 { int i; } __attribute__((aligned(16))) a2;

class X3 { int i; } a3 __attribute__((aligned(16)));

int main()
{
    PRINT_ALIGN(X, a);
    PRINT_ALIGN(X1, a1);
    PRINT_ALIGN(X2, a2);
    PRINT_ALIGN(X3, a3);
}
*/

// 根据__attribute__((aligned(16)))所在语句 位置的不同，对类和对象的作用是不同的。

////////////////////////////////

// MSVC
// MSVC的属性语法和GCC相似，它引入了一个__declspec扩展关键字，不过这个关键字没有以双下画线结尾，后面紧跟的是单对括号：
// __declspec(attribute-list)

// 相对于GCC复杂的属性语法规则，MSVC的属性语法规则就简单多了：

/*
__declspec(dllimport) class X {} varX;
class __declspec(dllimport) X {};
*/

////////////////////////////////////////////////////////////////////////////////////////

//////// c++11 标准推出的属性说明符格式
// [[attr1]] class [[attr2]] X { int i; } a, b[[attr3]];

/////// 使用using打开属性的命名空间
// 为了防止不同编译器厂商在扩展属性的时候发生冲突，标准属性的语法支持了命名空间
/*
[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]]
inline int f();
*/
// 或者
/*
[[gnu::always_inline, gnu::hot, gnu::const, nodiscard]]
inline int f();
*/

// C++17标准对命名空间属性声明做了优化，它引入了using关键字打开属性命名空间，随后即可直接使用命名空间的属
// 性从而减少代码冗余，其语法如下：
// [[ using attribute-namespace : attribute-list ]]
//
// c++17 之后可以这样简写写关于命名空间属性

/*
[[using gnu: always_inline, hot, const]][[nodiscard]]
inline int f();
*/

/// 上面说了 gcc和msvc 有关属性的自己定义的语法, 从c++11 开始出现了标准属性, 更加统一.
/// (因为各个编译器有不同的优化策略, 而且有些特性在标准中没有, 所以这并不代表你必须使用标准属性, 你也可以使用gcc/msvc等关于定义属性的语法)
//////////////////////////////////////////////////////////////////////////////////////////////
///               标准属性
///  虽然从语法上来说属性可以出现在程序的任意位置，但是从C++11到C++20标准一共只定义了9种标准属性。
///  这是因为C++标准委员会对于标准属性的定义非常谨慎。 一方面他们需要考虑一个语言特性应该定义为关键字还是定义为属性，
///  另一方面还需要谨慎考虑该属性是否是平台通用。 举例来说，在标准属性确定之前对齐字节长度一直作为一个扩展属性出现在各种编译器中，
///  但是C++标准并不认可这种情况，于是对齐字节长度作为语言本身的一部分出现在了新的标准当中。

/*
[[noreturn]](C++11)	指示函数不返回 (属性指示符)
[[carries_dependency]](C++11)	指示在函数内外传播“释放-消费”std::memory_order 中的依赖链 (属性指示符)
[[deprecated]](C++14)
[[deprecated("原因")]](C++14)	指示以此属性声明的名字或实体，允许使用但因某种 原因 而不鼓励使用 (属性指示符)
[[fallthrough]](C++17)	指示从前一 case 标号的直落是故意的，且会警告直落的编译器不应当对此诊断 (属性指示符)
[[maybe_unused]](C++17)	抑制对于未使用实体的编译器警告，如果有 (属性指示符)
[[nodiscard]](C++17)
[[nodiscard("原因")]](C++20)	鼓励编译器在返回值被丢弃时发出警告 (属性指示符)
[[likely]](C++20)
[[unlikely]](C++20)	指示编译器应当针对此种情况进行优化：通过某条语句的执行路径比其他任何执行路径更可能或不可能发生 (属性指示符)
[[no_unique_address]](C++20)	指示一个非静态数据成员不必具有与类中的其他所有非静态数据成员都不同的地址 (属性指示符)
[[assume(表达式)]](C++23)	指示 表达式 在给定的位置永远为 true (属性指示符)
[[indeterminate]](C++26)	指示对象在未初始化时具有不确定值 (属性指示符)
[[optimize_for_synchronized]](TM TS)	指示函数定义应当为从同步语句中调用的情况优化 (属性指示符)
*/

//////////////////////////////////////////////////////////////////////////////////////////////

/*
[[nodiscard]] 是C++17引入的一个属性，用于提示调用者不要忽略函数的返回值。这在需要确保返回值被处理的场景中非常有用，例如在错误处理、资源管理等情况下。

[[nodiscard]] 使用说明
你可以将 [[nodiscard]] 属性应用于函数、枚举类型和类。
*/

/*
// 函数返回值不可忽略：
[[nodiscard]] int compute_value() {
    return 42;
}
int main() {
    compute_value();  // 编译器可能会警告这里忽略了返回值
    int value = compute_value();  // 正确处理返回值
}
*/

/*
// 应用于类：
class [[nodiscard]] Error {
    public:
    Error(int code) : code_(code) {}
    int code() const { return code_; }
    private:
    int code_;
};

Error do_something() {
    return Error(1);
}

int main() {
    do_something();  // 编译器可能会警告这里忽略了返回值
    Error err = do_something();  // 正确处理返回值
}
*/

/*
应用于枚举类型：
enum class [[nodiscard]] Status {
    OK,
    Error
};

Status process() {
    return Status::Error;
}

int main() {
    process();  // 编译器可能会警告这里忽略了返回值
    Status s = process();  // 正确处理返回值
}
*/

/*
详细说明
目的：[[nodiscard]] 的主要目的是防止程序员忽略函数返回值，尤其是在返回值具有重要意义时（如错误码、资源句柄等）。
编译器支持：大多数现代C++编译器（如GCC、Clang、MSVC）都支持 [[nodiscard]] 属性，并在忽略返回值时生成警告。
提高代码质量：通过强制处理返回值，[[nodiscard]] 帮助开发者编写更健壮、更可靠的代码，减少因忽略重要返回值而导致的潜在错误。
使用场景
错误处理：当函数返回错误码时，[[nodiscard]] 确保调用者不会忽略这些错误。
资源管理：对于返回需要手动管理的资源（如文件句柄、内存指针等）的函数，使用 [[nodiscard]] 确保调用者适当处理这些资源。
重要计算结果：当函数返回的结果对程序逻辑至关重要时，使用 [[nodiscard]] 确保结果被正确使用。
总结
[[nodiscard]] 属性是一个简单但非常有用的工具，帮助开发者避免忽略重要的返回值，从而提高代码的健壮性和可靠性。
通过在适当的地方使用 [[nodiscard]]，可以显著减少因忽略返回值而导致的错误。
*/

//////////////////////////////////////////////////////////////////////////////////////////////

/*
[[likely]]和[[unlikely]]属性用于提示编译器某个分支的执行概率，帮助编译器进行优化。

优化提示：[[likely]]和[[unlikely]]只是优化提示，编译器可以选择忽略它们。
实际效果：不同的编译器和不同的优化级别可能会对这些提示有不同的处理方式。需要在实际应用中进行性能测试，以确认提示的效果。
可读性：使用这些属性时应谨慎，确保不会影响代码的可读性和可维护性。
这些提示不会改变程序的语义，但可能影响生成的机器代码的性能。
*/

/*最后解释一下no_unique_address这个属性的使用场景。
实现一些必要的函数，常见的是STL中一些算法函数所需的函数对象（仿函数）。而这种类作为数据成员加入其他类时，会占据独一无二
的内存地址，实际上这是没有必要的。所以，在C++20的环境下，我们可以使用no_unique_address属性，让其不需要占用额外的内存地
址空间。*/

//////////////////////////////////////////////////////////////////////////////////////////////

// no_unique_address是C++20标准引入的属性，该属性指示编译器该数据成员不需要唯一的地址，也就是说它不需要与其他非静态
// 数据成员使用不同的地址。注意，该属性声明的对象必须是非静态数据成员且不为位域：

/*
struct Empty {};

struct X {
    int i;
    Empty e;
};

// main函数
std::cout << "sizeof(X) = " << sizeof(X) << std::endl
  << "X::i address = " <<  &((X*)0)->i << std::endl
  << "X::e address = " << &((X*)0)->e;
以上代码的输出结果如下：
sizeof(X) = 8
X::i address = 0
X::e address = 0x4
*/

// 由此可见，即使结构体Empty为空，但是在X中依然也占据了唯一地址。现在让我们给Empty e添加no_unique_address属性：

/*
struct X {
    int i;
    [[no_unique_address ]]Empty e;
};
有了这个属性，编译器得知e不需要独立地址，于是将数据成员i和e编译在了同样的地址：
sizeof(X) = 4
X::i address = 0
X::e address = 0
*/
